#ifndef GX_GRAMMAR_H
#define GX_GRAMMAR_H

#include "Basic/GxLibBasic.h"
#include "Regex/MetaLexer.h"

namespace GxMetaParser
{
	class Grammar;

	class SymbolObject;
	class ClientSymbolObject;

	struct Token
	{
		String Str;
		int Position;
		int Type;

		Token & operator = (const Token & tk);
	};

	class ClientSymbolObject : public Object
	{
	public:

	};

	template <typename T>
	class ClientSymbolObjectWrapper : public ClientSymbolObject
	{
	public:
		T Value;
	};

	class SymbolObject : public Object
	{
	public:
		RefPtr<ClientSymbolObject> ClientObject;
		Token TokenData;
		int SymbolType;
	};

	class GrammarSymbol : public Object
	{
	public:
		enum SymbolType
		{
			Terminal, NonTerminal, Epsilon, End, Error
		};
		enum AssociativeProperty
		{
			Left, Right, Non
		};
		String Name;
		SymbolType Type;
		int Priority;
		AssociativeProperty Associative;
	public:
		GrammarSymbol();
	
	};

	class Grammar;

	class Item
	{
	public:
		int ProductionRule;
		int Position;
		int NextChar;
		bool operator > (const Item & item);
		bool operator < (const Item & item);
		bool operator == (const Item & item);
	};

	class ItemSet: public Object
	{
	private:
		static const int PositionHashSize = 2;
		static const int CharHashSize = 4;
		int RuleCount;
		Array<Array<Item, true>> ItemsHash;
	public:
		Array<Item, true> Items;
		Array<int> Goto;
	public:
		//bool operator == (const ItemSet & I);
		void Clear();
		bool ItemExists(const Item & item) const;
		bool CombineItem(const Item & item);
		void GetNextSymbols(OrderedArray<int> & symbols, Grammar * g);
		ItemSet(Grammar * g);
	};

	class Action
	{
	public:
		enum ActionType
		{
			Error,
			Shift,
			Reduct,
			Accept
		};
		ActionType Type;
		int ID;
		int Rule;
		Action();
		
	};

	class Automaton : public Object
	{
	public:
		Array<Array<Action>> Actions;
		Array<Array<int>> Goto;
	};

	class ProductionRule;

	struct ReductionEventArgs
	{
		ProductionRule * Rule;
		Array<RefPtr<SymbolObject>> SymbolObjects;
		RefPtr<ClientSymbolObject> ReturnObject;
		template<typename T>
		void SetObject(int id, const T & value)
		{
			ClientSymbolObjectWrapper<T> * val = new ClientSymbolObjectWrapper<T>();
			val->Value = value;
			SymbolObjects[id]->ClientObject = val;
		}
		template<typename T>
		T & GetObject(int id)
		{
			return ((ClientSymbolObjectWrapper<T>*)(SymbolObjects[id]->ClientObject.operator->()))->Value;
		}
		template<typename T>
		T & GetObject(int id, T& val)
		{
			val = ((ClientSymbolObjectWrapper<T>*)(SymbolObjects[id]->ClientObject.operator->()))->Value;
			return val;
		}
		template<typename T>
		Array<T> & GetList(int id)
		{
			return *(((ClientSymbolObjectWrapper<RefPtr<Array<T>>>*)(SymbolObjects[id]->ClientObject.operator->()))->Value);
		}
		template <typename T>
		void Return(const T & value)
		{
			ClientSymbolObjectWrapper<T> * val = new ClientSymbolObjectWrapper<T>();
			val->Value = value;
			ReturnObject = val;
		}
	};

	typedef Event<ReductionEventArgs&> ReductionEventHandler;

	class ProductionRule: public Object
	{
	public:
		int NonTerminalID;
		Array<int> Symbols;
		String GroupName;
		int Priority;
		ReductionEventHandler OnReduction;
		ProductionRule();
		GrammarSymbol::AssociativeProperty Associative;
	};

	class GrammarError
	{
	public:
		int Symbol;
		int State;
		Action OldAction;
		Action NewAction;
		String ToString(Grammar * g);
	};

	class GrammarParser;

	class Grammar : public Object
	{
	private:
		int ExtendedStartRule;
		RefPtr<GrammarParser> Parser;
	public:
		RefPtr<Automaton> ParseTable;
		
		Array<OrderedArray<int>> FirstSet, FollowSet;
		Array<RefPtr<ItemSet>> ItemSets;
		Array<GrammarSymbol> Symbols;
		Array<RefPtr<ProductionRule> > Rules;
		Array<GrammarError> Errors;
		int StartRule;
	private:
		void EvalFirstSetOfSymbol();
		//bool EvalFirstElement(int symbol, OrderedArray<int> & set, bool addEpsilon); // Returns whether epsilon is added.
		bool EvalFirstSet(const Array<int> & prefix, OrderedArray<int> & set, bool addEpsilon);
		void EvalFollowSet();
		void EvalClosure(ItemSet & I);
		void EvalGotoSet(const ItemSet & I, int X, ItemSet & J, bool closure = true);
		void EvalItemSets();
		void SetAction(int i, int a, const Action & act);
		void CreateAutomaton();
		int RegisterSymbol(const String & name, GrammarSymbol::SymbolType type);
		
	public:
		Grammar();
		int GetEpsilonSymbolID();
		int GetEndSymbolID();
		int GetErrorSymbolID();
		int GetTerminalSymbolCount();
		void Initialize(bool validate = false);
		int RegisterTerminalSymbol(const String & name);
		int RegisterNonTerminalSymbol(const String & name);
		GrammarSymbol * GetRegisteredSymbol(const String & name);
		int GetSymbolID(const String & name);
		bool ParseProfile(const String & profile);
		bool ValidateGrammar();
		bool AddRule(const String & rule);
		bool AddRule(const String & rule, const ReductionEventHandler & handler);
	};

	class GrammarSyntaxError
	{
	public:
		String Text;
		int Position;
	};

	class GrammarParser : public Object
	{
	private:
		enum TokenType
		{
			tkGroup, tkPriority, tkEpsilon, tkLeft, tkRight, tkNonAssoc, tkError,
			tkSymbolName, tkEquals, tkLineBreak, tkLBrace, tkRBrace, tkNumber
		};
		enum SymbolState
		{
			NotExist, Terminal, NonTerminal
		};
		Grammar * grammar;
		RefPtr<MetaLexer> lexer;
		LexStream::LinkNode * curNode;
		bool ParseRule();
		LexStream::LinkNode * ReadToken(TokenType type);
		SymbolState GetSymbolState(const String & str);
		void GenerateError(const String & text, int pos); 
	public:
		Array<GrammarSyntaxError> Errors;
		GrammarParser(Grammar * g);
		bool Parse(const String & str);	
	};
}

#endif